/*  Copyright (c) 2012 William Rogers and James Boud

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

using System;
//using SurfaceFormat = Microsoft.Xna.Framework.Graphics.SurfaceFormat;
using DepthFormat = Microsoft.Xna.Framework.Graphics.DepthFormat;
using RenderTargetUsage = Microsoft.Xna.Framework.Graphics.RenderTargetUsage;
using SlimDX;
using SlimDX.Direct3D11;

namespace JBBRXG11
{
    /// <summary>
    /// Contains a 2D texture that can be used as a render target.
    /// </summary>
    public class RenderTarget2D : Texture2D, IDisposable
    {
        SlimDX.Direct3D11.RenderTargetView dxtargetview;
        Viewport myviewport;
        SlimDX.Direct3D11.Texture2D dxdepthbuffer;
        SlimDX.Direct3D11.DepthStencilView dxdepthstencilview;

        internal SlimDX.Direct3D11.RenderTargetView DX_TargetView { get { return dxtargetview; } }

        internal Viewport RenderViewport { get { return myviewport; } }
        internal SlimDX.Direct3D11.DepthStencilView DX_DepthStencilView { get { return dxdepthstencilview; } }

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////// Constructors ////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////

        internal static void MakeDepthBuffer(GraphicsDevice graphicsDevice,
                                             int width, int height, DepthFormat preferredDepthFormat,
                                             out SlimDX.Direct3D11.Texture2D dxdepthbuffer,
                                             out SlimDX.Direct3D11.DepthStencilView dxdepthstencilview)
        {
            if (preferredDepthFormat == DepthFormat.None)
            {
                // TODO:  untested - don't know what the consequence of this might be
                dxdepthbuffer = null;
                dxdepthstencilview = null;
                return;
            }
            // Allocate a buffer the same shape and size as the RenderTarget
            Texture2DDescription depthbufferdesc;
            depthbufferdesc = new Texture2DDescription();
            depthbufferdesc.Width = width;
            depthbufferdesc.Height = height;
            depthbufferdesc.MipLevels = 1;
            depthbufferdesc.ArraySize = 1;
            depthbufferdesc.Format = EnumConversions.convert(preferredDepthFormat);
            depthbufferdesc.SampleDescription = new SlimDX.DXGI.SampleDescription(1, 0);
            depthbufferdesc.Usage = ResourceUsage.Default;
            depthbufferdesc.BindFlags = BindFlags.DepthStencil;
            depthbufferdesc.CpuAccessFlags = CpuAccessFlags.None;
            depthbufferdesc.OptionFlags = ResourceOptionFlags.None;
            dxdepthbuffer = new SlimDX.Direct3D11.Texture2D(graphicsDevice.DX_Device, depthbufferdesc);

            DepthStencilViewDescription depthstencildesc;
            depthstencildesc = new DepthStencilViewDescription();
            depthstencildesc.Dimension = DepthStencilViewDimension.Texture2D;
            depthstencildesc.Flags = DepthStencilViewFlags.None;
            depthstencildesc.Format = depthbufferdesc.Format;
            depthstencildesc.MipSlice = 0;
            dxdepthstencilview = new DepthStencilView(graphicsDevice.DX_Device, dxdepthbuffer, depthstencildesc);
        }

        /// <summary>
        /// Creates an instance of this object.
        /// </summary>
        /// <param name="graphicsDevice">The graphics device to associate with this
        /// render target resource.</param>
        /// <param name="width">Width, in pixels, of the render target. You can use
        /// graphicsDevice.PresentationParameters.BackBufferWidth to get the current
        /// screen width.</param>
        /// <param name="height">Height, in pixels, of the render target. You can use
        /// graphicsDevice.PresentationParameters.BackBufferHeight to get the current
        /// screen height.</param>
        public RenderTarget2D(GraphicsDevice graphicsDevice, int width, int height)
            : base(graphicsDevice, width, height, false, Microsoft.Xna.Framework.Graphics.SurfaceFormat.Color, true)
        {
            dxtargetview = new RenderTargetView(graphicsDevice.DX_Device, dxtexture);
            myviewport = new Viewport(0, 0, width, height);
            MakeDepthBuffer(graphicsDevice, width, height, GraphicsDeviceManager.DefaultDepthStencilFormat, out dxdepthbuffer, out dxdepthstencilview);
            GraphicsDeviceManager.GetGame(Game.GraphicsDevice).UserContentAdd(this);
        }

        /// <summary>
        /// Creates an instance of this object.
        /// </summary>
        /// <param name="graphicsDevice">The graphics device to associate with this
        /// render target resource.</param>
        /// <param name="width">Width, in pixels, of the render target. You can use
        /// graphicsDevice.PresentationParameters.BackBufferWidth to get the current
        /// screen width.</param>
        /// <param name="height">Height, in pixels, of the render target. You can use
        /// graphicsDevice.PresentationParameters.BackBufferHeight to get the current
        /// screen height.</param>
        /// <param name="mipMap">[MarshalAsAttribute(U1)] True to enable a full
        /// mipmap chain to be generated, false otherwise.</param>
        /// <param name="preferredFormat">Preferred format for the surface data. This
        /// is the format preferred by the application, which may or may not be
        /// available from the hardware.</param>
        /// <param name="preferredDepthFormat">Preferred format for the depth buffer.
        /// This is the format preferred by the application, which may or may not be
        /// available from the hardware.</param>
        public RenderTarget2D(GraphicsDevice graphicsDevice, int width, int height, bool mipMap, Microsoft.Xna.Framework.Graphics.SurfaceFormat preferredFormat, DepthFormat preferredDepthFormat)
            : base(graphicsDevice, width, height, mipMap, preferredFormat, true)
        {
            // TODO:  do something with preferred DepthFormat
            dxtargetview = new RenderTargetView(graphicsDevice.DX_Device, dxtexture);
            myviewport = new Viewport(0, 0, width, height);
            MakeDepthBuffer(graphicsDevice, width, height, preferredDepthFormat, out dxdepthbuffer, out dxdepthstencilview);
            GraphicsDeviceManager.GetGame(Game.GraphicsDevice).UserContentAdd(this);
        }

        /// <summary>
        /// Creates an instance of this object.
        /// </summary>
        /// <param name="graphicsDevice">The graphics device to associate with this
        /// render target resource.</param>
        /// <param name="width">Width, in pixels, of the render target. You can use
        /// graphicsDevice.PresentationParameters.BackBufferWidth to get the current
        /// screen width.</param>
        /// <param name="height">Height, in pixels, of the render target. You can use
        /// graphicsDevice.PresentationParameters.BackBufferHeight to get the current
        /// screen height.</param>
        /// <param name="mipMap">[MarshalAsAttribute(U1)] True to enable a full
        /// mipmap chain to be generated, false otherwise.</param>
        /// <param name="preferredFormat">Preferred format for the surface data. This
        /// is the format preferred by the application, which may or may not be
        /// available from the hardware.</param>
        /// <param name="preferredDepthFormat">Preferred format for the depth buffer.
        /// This is the format preferred by the application, which may or may not be
        /// available from the hardware.</param>
        /// <param name="preferredMultiSampleCount">The preferred number of
        /// multisample locations.</param>
        /// <param name="usage">Behavior options.</param>
        public RenderTarget2D(GraphicsDevice graphicsDevice, int width, int height, bool mipMap, Microsoft.Xna.Framework.Graphics.SurfaceFormat preferredFormat, DepthFormat preferredDepthFormat, int preferredMultiSampleCount, RenderTargetUsage usage)
        {
            // TODO:  do something with preferred DepthFormat and multisampling
            dxtargetview = new RenderTargetView(graphicsDevice.DX_Device, dxtexture);
            myviewport = new Viewport(0, 0, width, height);
            MakeDepthBuffer(graphicsDevice, width, height, preferredDepthFormat, out dxdepthbuffer, out dxdepthstencilview);
            GraphicsDeviceManager.GetGame(Game.GraphicsDevice).UserContentAdd(this);
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////// Properties //////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>
        /// Determines if the index buffer data has been lost due to a lost device
        /// event.
        /// </summary>
        public bool IsContentLost { get { throw new Exception("NYI"); } }

        /// <summary>
        /// Gets or sets the render target usage.
        /// </summary>
        public RenderTargetUsage RenderTargetUsage { get { throw new Exception("NYI"); } }

        /// <summary>
        /// Gets the number of sample locations during multisampling.
        /// </summary>
        public int MultiSampleCount { get { throw new Exception("NYI"); } }

        /// <summary>
        /// Gets the data format for the depth stencil data.
        /// </summary>
        public DepthFormat DepthStencilFormat { get { throw new Exception("NYI"); } }

        //////////////////////////////////////////////////////////////////////////////////////////////////////////////
        //////////////////////////////////////// Methods /////////////////////////////////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////////////////////////////////

        public virtual void add_ContentLost(EventHandler<EventArgs> value)
        {
            throw new Exception("Not Yet Implemented");
        }

        public virtual void remove_ContentLost(EventHandler<EventArgs> value)
        {
            throw new Exception("Not Yet Implemented");
        }

        protected virtual void raise_ContentLost(Object value0, EventArgs value1)
        {
            throw new Exception("Not Yet Implemented");
        }

        public new void Dispose()
        {
            if (dxdepthstencilview != null) dxdepthstencilview.Dispose();
            if (dxdepthbuffer != null) dxdepthbuffer.Dispose();
            dxtargetview.Dispose();
            base.Dispose();
        }
    }
}
